#pragma once
#ifndef __RANDFAST_HEADER__
#define __RANDFAST_HEADER__

//////////////////////////////////////////////////////////////////////////

#include <limits>
#include <ctime>
#include <cassert>

//////////////////////////////////////////////////////////////////////////

template <typename destT, typename srcT>
inline destT& absolute_cast(srcT& x)
{
	return reinterpret_cast<destT&>(x);
}

template <typename destT, typename srcT>
inline const destT& absolute_cast(const srcT& x)
{
	return reinterpret_cast<const destT&>(x);
}

//////////////////////////////////////////////////////////////////////////

class RandGenFast
{
private:
	unsigned long int m_Seed;

public:
	RandGenFast();

	unsigned long int RandPosInt(); // [0, 2^32 - 1]
	long int RandInt(); // [-2^31 + 1, 2^31 - 1]
	unsigned long int RandPosInt(unsigned long int _n); // [0, n] for n < (2^32 - 1)
	long int RandInt(long int _n); // [-n, n] for n < (2^31 - 1)
	long int RandInt(long int _a, long int _b); // [a, b] for a,b < (2^31 - 1)

	float RandPosFloat(); // [0.0, MAX_FLOAT]
	float RandFloat(); // [-MAX_FLOAT/2, MAX_FLOAT/2]
	float RandPosFloat(float _n); // [0.0, n]
	float RandFloat(float _n); // [-n, n]
	float RandFloat(float _a, float _b); // [a, b]

	bool RandBool();

	unsigned long int GetSeed() const;
	void SetSeed(unsigned long int _seed);
};

//////////////////////////////////////////////////////////////////////////

extern RandGenFast rnd;

inline RandGenFast::RandGenFast()
{
	m_Seed = (unsigned)std::time(0);
}

inline unsigned long int RandGenFast::RandPosInt()
{
	return ( m_Seed = (m_Seed * 196314165 + 907633515) );
}
inline long int RandGenFast::RandInt()
{
	return static_cast<long int>( RandPosInt() );
}
inline unsigned long int RandGenFast::RandPosInt(unsigned long int _n)
{
	return (RandPosInt() % _n);
}
inline long int RandGenFast::RandInt(long int _n)
{
	assert( _n >= 0 );
	return ( RandPosInt(_n) * (RandBool()? 1 : -1) );
}
inline long int RandGenFast::RandInt(long int _a, long int _b)
{
	assert(_a <= _b && "First argument has to be lower than the second argument.");
	return ( _a + RandPosInt( _b - _a + 1 ) );
}

inline float RandGenFast::RandPosFloat()
{
	return RandPosFloat( std::numeric_limits<float>::max() );
}
inline float RandGenFast::RandFloat()
{
	return ( RandPosFloat() * (RandBool()? 1 : -1) );
}
inline float RandGenFast::RandPosFloat(float _n)
{
	assert( _n >= 0 );
	return (absolute_cast<float>(RandPosInt() & 0x007FFFFF | 0x3F800000) - 1.0f) * _n;
}
inline float RandGenFast::RandFloat(float _n)
{
	return ( RandPosFloat(_n) * (RandBool()? 1 : -1) );
}
inline float RandGenFast::RandFloat(float _a, float _b)
{
	assert(_a <= _b && "First argument has to be lower than the second argument.");
	return ( _a + RandPosFloat(_b - _a) );
}

inline bool RandGenFast::RandBool()
{
	return ( RandPosInt() > 1<<31 );
}

inline unsigned long int RandGenFast::GetSeed() const
{
	return m_Seed;
}
inline void RandGenFast::SetSeed(unsigned long int _seed)
{
	m_Seed = _seed;
}

//////////////////////////////////////////////////////////////////////////

#endif // #ifndef __RANDFAST_HEADER__